////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2017-2018 Qualcomm Technologies, Inc.
// All Rights Reserved.
// Confidential and Proprietary - Qualcomm Technologies, Inc.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// THIS IS AUTO-GENERATED CODE.  PLEASE DO NOT EDIT (File bug reports against tools).
///
/// Auto-generated by: ParameterParser V1.1.4
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @file  parametersetmanager.h
/// @brief Auto-generated Chromatix parameter file
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef PARAMETERSETMANAGER_H
#define PARAMETERSETMANAGER_H

#include "parametertuningtypes.h"
#include "parameterfilesymboltableentry.h"
#include "parameterfilesymboltable.h"
#include <utils/Log.h>


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// class ModeEntry
///
/// @brief Contains an mode tree data.
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class ModeEntry
{
public:
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// Constructor
    ///
    /// @brief  Constructs the ModeEntry class
    ///
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ModeEntry()
    {
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// Destructor
    ///
    /// @brief  Destructs the ModeEntry class
    ///
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ~ModeEntry()
    {
        FirstChild = NULL;
        LastChild  = NULL;
        FirstGroup = NULL;
        LastGroup  = NULL;

        Next = NULL;

        PARAMETER_DELETE FirstModule;
        FirstModule = NULL;
        LastModule  = NULL;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// Initialize
    ///
    /// @brief  Initializes the ModeEntry class
    ///
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID Initialize(UINT32 id, UINT16 mode, UINT16 subMode)
    {
        Id          = id;
        Mode        = mode;
        SubMode     = subMode;
        Parent      = NULL;
        FirstChild  = NULL;
        LastChild   = NULL;
        FirstGroup  = NULL;
        LastGroup   = NULL;
        Next        = NULL;
        FirstModule = NULL;
        LastModule  = NULL;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// Add
    ///
    /// @brief  Adds a child ModeEntry
    ///
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID AddMode(ModeEntry* child)
    {
        if (child != NULL)
        {
            child->Parent = this;

            if (child->Mode == Mode && child->SubMode == SubMode)
            {
                // This mode is the same mode/sub mode as its parent, therefore it is differentiated by group.
                // Group value is not part of the ModeEntry class because the actual group value does not matter,
                // since modules in different groups are also differentiated by name.
                // Group modes hold the actual module data for the mode.
                if (FirstGroup == NULL)
                {
                    FirstGroup = child;
                }
                else
                {
                    LastGroup->Next = child;
                }
                LastGroup = child;
            }
            else
            {
                // Child modes define the mode tree structure
                if (FirstChild == NULL)
                {
                    FirstChild = child;
                }
                else
                {
                    LastChild->Next = child;
                }
                LastChild  = child;
            }
        }
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// Add
    ///
    /// @brief  Adds a Module
    ///
    /// @return Returns the ModeEntry object or NULL, if not found
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    BOOL AddModule(ParameterModule* module)
    {
        if (module != NULL)
        {
            if (FirstModule == NULL)
            {
                FirstModule = module;
            }
            else
            {
                LastModule->Next = module;
            }

            LastModule  = module;
        }

        return module != NULL;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// FindMode
    ///
    /// @brief  Finds a ModeEntry, in the mode tree, from a mode id
    ///
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ModeEntry* FindMode(UINT32 id)
    {
        ModeEntry* result = NULL;
        if (Id == id)
        {
            result = this;
        }
        else
        {
            ModeEntry* child = FirstGroup;
            while (result == NULL && child != NULL)
            {
                result = child->FindMode(id);
                child  = child->Next;
            }

            if (result == NULL)
            {
                child = FirstChild;
                while (result == NULL && child != NULL)
                {
                    result = child->FindMode(id);
                    child = child->Next;
                }
            }
        }

        return result;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// FindMode
    ///
    /// @brief  Finds a ModeEntry, in children, from a mode, subMode
    ///
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ModeEntry* FindMode(TuningMode* mode)
    {
        ModeEntry* result = NULL;

        ModeEntry* child = FirstChild;
        while (result == NULL && child != NULL)
        {
            if ((UINT16)mode->mode  == child->Mode &&
                mode->subMode.value == child->SubMode)
            {
                result = child;
            }
            else
            {
                child = child->Next;
            }
        }

        return result;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// FindMode
    ///
    /// @brief  Finds a ModeEntry, in children, from a mode/subMode array
    ///
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ModeEntry* FindMode(TuningMode* modes, UINT32 count, UINT32 found)
    {
        ModeEntry* result = NULL;

        for (UINT32 index = 0; result == NULL && index < count; index++)
        {
            result = FindMode(&modes[index]);

            if (result != NULL && found + 1 < count)
            {
                result = result->FindMode(modes, count, found + 1);
            }
        }

        return result;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// FindModule
    ///
    /// @brief  Finds a Module from a module name id
    ///
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ParameterModule* FindModule(const CHAR* moduleName)
    {
        ParameterModule* result = NULL;

        ParameterModule* module = FirstModule;
        while (result == NULL && module != NULL)
        {
            if (PARAMETER_STRCMP(module->Name, moduleName) == 0)
            {
                result = module;
            }
            module = module->Next;
        }

        ModeEntry* child = FirstGroup;
        while (result == NULL && child != NULL)
        {
            result = child->FindModule(moduleName);
            child = child->Next;
        }
        return result;
    }

    // Mode ID
    UINT32 Id;

    // Mode
    UINT16 Mode;

    // Sub Mode
    UINT16 SubMode;

    // Parent Mode
    ModeEntry* Parent;

    // First Child
    ModeEntry* FirstChild;

    // Last Child
    ModeEntry* LastChild;

    // First Group Mode
    ModeEntry* FirstGroup;

    // Last Group Mode
    ModeEntry* LastGroup;

    // Next mode
    ModeEntry* Next;

    // First Module
    ParameterModule* FirstModule;

    // Last Module
    ParameterModule* LastModule;
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ParameterSetManager
///
/// @brief Manages the parameter set
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class ParameterSetManager
{
public:
    // Constants as defined in Chromatix Data Access Design document
    const CHAR*  TAG             = "QTI Chromatix Header";
    const UINT32 TAG_LEN         = 28;
    const UINT32 TOOL_LEN        = 48;
    const UINT32 BINARY_TAG_LEN  = 64;
    const UINT16 MAJOR           = 1;
    const UINT16 MINOR           = 0;
    const UINT32 MODE_TREE_PATCH = 2;
    const UINT32 PATCH           = 3;
    const UINT32 MIN_SECTIONS    = 2;
    const UINT32 SECTION_LEN     = 12;
    const UINT32 SYMBOL_SECTION  = 0;
    const UINT32 DATA_SECTION    = 1;
    const UINT32 MODE_SECTION    = 2;
    const UINT32 SIZE_OFFSET     = TAG_LEN;  // Offset to write the file size

    static const INT32 ERROR_LEN = 1024;

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// LoadBinaryParameters
    ///
    /// @brief Loads binary parameters from a buffer
    ///
    /// @param buffer   Binary parameter buffer
    /// @param length   Length of the binary parameter buffer
    ///
    /// @return Node
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    BOOL LoadBinaryParameters(
        UINT8* buffer,
        UINT64 length)
    {
        UINT64 pos    = 0;
        UINT32 offset = 0;
        UINT32 count  = 0;

        Valid = FALSE;
        ALOGE("%s:%d E", __func__, __LINE__);

        if (ReadHeader(buffer, length, &pos, &offset, &count))
        {
            pos   = (INT32)offset;
            Valid = ReadSections(buffer, length, &pos, count);
        }

        return Valid;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// GetModule
    ///
    /// @brief Returns first parameter module matching the name, mode, selector, and selectorData
    ///
    /// @param name         Module name
    /// @param mode         Mode
    /// @param selector     Selector
    /// @param selectorData Selector data
    ///
    /// @return The first parameter module found matching the name and mode.  Version is ignored.
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ParameterModule* GetModule(
        const CHAR* name,
        TuningMode* modeBranch,
        UINT32      modeCount)
    {
        ParameterModule* result = NULL;

        if (m_rootMode != NULL)
        {
            ModeEntry* mode = m_rootMode;

            result = mode->FindModule(name);

            // First mode must always been root, start at index 1
            UINT32 index = 1;
            while (mode != NULL && index < modeCount)
            {
                UINT32 sameModeCount = 1;

                while (index + sameModeCount < modeCount && modeBranch[index].mode == modeBranch[index + sameModeCount].mode)
                {
                    sameModeCount++;
                }

                mode = mode->FindMode(&modeBranch[index], sameModeCount, 0);

                if (mode != NULL)
                {
                    ParameterModule* module = mode->FindModule(name);
                    if (module != NULL)
                    {
                        result = module;
                    }
                }

                index += sameModeCount;
            }
        }
        return result;
    }

    BOOL Valid;
    CHAR Error[ERROR_LEN];

protected:
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// ParameterSetManager
    ///
    /// @brief Constructor
    ///
    /// @return Node
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ParameterSetManager()
    {
        Valid      = FALSE;
        Error[0]   = '\0';
        m_rootMode = NULL;
        m_modes    = NULL;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// ~ParameterSetManager
    ///
    /// @brief Destructor
    ///
    /// @return Node
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ~ParameterSetManager()
    {
        if (m_modes != NULL)
        {
            PARAMETER_DELETE[]  m_modes;
            m_modes = NULL;
        }
    }

private:

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// ReadHeader
    ///
    /// @brief Reads and validates the the binary parameter file header
    ///
    /// @param buffer   Binary parameter buffer
    /// @param length   Length of the binary parameter buffer
    /// @param pos      Current position in the the binary parameter buffer [in/out]
    /// @param offset   Offset to the section buffer [out]
    /// @param count    Section count [out]
    ///
    /// @return True if successful
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    BOOL ReadHeader(
        UINT8*  buffer,
        UINT64  length,
        UINT64* pos,
        UINT32* offset,
        UINT32* count)
    {
        BOOL   result    = TRUE;
        CHAR*  tag       = NULL;
        CHAR*  tool      = NULL;
        CHAR*  binaryTag = NULL;
        CHAR   error[ERROR_LEN];
        UINT32 size      = 0;
        UINT16 major     = 0;
        UINT16 minor     = 0;
        UINT32 patch     = 0;

        error[0] = '\0';
        tag      = (CHAR*)buffer;
        *pos    += TAG_LEN;
        result   = result && ParameterUtils::ReadUInt32(&size,  buffer, length, pos);
        result   = result && ParameterUtils::ReadUInt16(&major, buffer, length, pos);
        result   = result && ParameterUtils::ReadUInt16(&minor, buffer, length, pos);
        result   = result && ParameterUtils::ReadUInt32(&patch, buffer, length, pos);

        if (major == MAJOR && minor == MINOR && patch > MODE_TREE_PATCH)
        {
            tool      = (CHAR*)(buffer + *pos);
            *pos     += TOOL_LEN;
            binaryTag = (CHAR*)(buffer + *pos);
            *pos     += BINARY_TAG_LEN;
        }

        result = result && ParameterUtils::ReadUInt32(offset, buffer, length, pos);
        result = result && ParameterUtils::ReadUInt32(count,  buffer, length, pos);

        if (result)
        {
            if (PARAMETER_STRCMP(tag, TAG) != 0)
            {
                PARAMETER_SPRINTF(error, ERROR_LEN, "Invalid binary header tag: %s", tag);
                SetError(error);
            }
            else if (size > length)
            {
                PARAMETER_SPRINTF(error, ERROR_LEN, "Binary header data too small: Expected: %d bytes, Actual: %u bytes", size, (UINT32)length);
                SetError(error);
                result = FALSE;
            }
            else if (major == MAJOR && minor == MINOR && patch < MODE_TREE_PATCH)
            {
                PARAMETER_SPRINTF(error, ERROR_LEN, "Warning: This is an older binary format, only Default mode data is supported.");
                PARAMETER_SPRINTF(error, ERROR_LEN, "To support multiple modes, please re-generate binary using the latest version of Chromatix");
                SetError(error);
            }
            else if (major != MAJOR || minor != MINOR || patch > PATCH)
            {
                PARAMETER_SPRINTF(error, ERROR_LEN, "Unsupported binary header version: %u.%u.%u.", major, minor, patch);
                PARAMETER_SPRINTF(error, ERROR_LEN, "Please re-generate parser code  using the latest version of Chromatix");
                SetError(error);
                result = FALSE;
            }
        }
        else
        {
            SetError("General error parsing binary header data header");
        }

        return result;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// AddModule
    ///
    /// @brief Updates the module in the module table, or adds new module if not found.
    ///
    /// @param module   Parameter module to add of update
    ///
    /// @return Node
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    BOOL AddModule(
        ParameterModule* module)
    {
        BOOL result = FALSE;

        if (m_rootMode != NULL && module != NULL)
        {
            ModeEntry* mode = &m_modes[module->ModeId];

            if (mode != NULL)
            {
                mode->AddModule(module);
                result = TRUE;
            }
        }

        return result;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// ReadSections
    ///
    /// @brief Reads and validates the the binary parameter file section table
    ///
    /// @param buffer   Binary parameter buffer
    /// @param length   Length of the binary parameter buffer
    /// @param pos      Current position in the the binary parameter buffer [in/out]
    /// @param count    Section count
    ///
    /// @return True if successful
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    BOOL ReadSections(
        UINT8*  buffer,
        UINT64  length,
        UINT64* pos,
        UINT32  count)
    {
        BOOL result = TRUE;

        if (count >= MIN_SECTIONS)
        {
            UINT32 symbolOffset = 0;
            UINT32 symbolSize   = 0;
            UINT32 modeOffset   = 0;
            UINT32 modeSize     = 0;
            UINT32 dataOffset   = 0;
            UINT32 dataSize     = 0;
            ALOGE("Liubo count: %d", count);

            for (UINT32 i = 0; i < count; i++)
            {
                UINT32 type   = 0;
                UINT32 offset = 0;
                UINT32 size   = 0;

                result = result && ParameterUtils::ReadUInt32(&type,   buffer, length, pos);
                result = result && ParameterUtils::ReadUInt32(&offset, buffer, length, pos);
                result = result && ParameterUtils::ReadUInt32(&size,   buffer, length, pos);

                if (type == SYMBOL_SECTION)
                {
                    symbolSize   = size;
                    symbolOffset = offset;
                }
                else if (type == DATA_SECTION)
                {
                    dataSize   = size;
                    dataOffset = offset;
                }
                else if (type == MODE_SECTION)
                {
                    modeSize   = size;
                    modeOffset = offset;
                }
            }

            ALOGE("LiuBo, symbolSize: %d offset: %d", symbolSize, symbolOffset);
            ALOGE("LiuBo, dataSize: %d offset: %d", dataSize, dataOffset);
            ALOGE("LiuBo, modeSize: %d offset: %d", modeSize, modeOffset);

            if (modeSize > 0 && modeOffset > 0)
            {
                // Read Mode Table
                UINT32 modCount = modeSize / ParameterFileSymbolTableEntry::MODE_ENTRY_SIZE;
                UINT32 expected = modCount * ParameterFileSymbolTableEntry::MODE_ENTRY_SIZE;
                BOOL   valid    = modeSize == expected;
                ALOGE("LiuBo, modCount: %d", modCount);

                if (valid)
                {
                    UINT64 modePos = (UINT64)modeOffset;
                    m_modes        = PARAMETER_NEW ModeEntry[modCount];


                    for (UINT32 i = 0; i < modCount; i++)
                    {
                        UINT32 id;
                        UINT16 mode;
                        UINT16 subMode;
                        UINT32 group;
                        UINT32 parentID;
                        valid = valid & ParameterUtils::ReadUInt32(&id,       buffer, length, &modePos);
                        valid = valid & ParameterUtils::ReadUInt16(&mode,     buffer, length, &modePos);
                        valid = valid & ParameterUtils::ReadUInt16(&subMode,  buffer, length, &modePos);
                        valid = valid & ParameterUtils::ReadUInt32(&group,    buffer, length, &modePos);
                        valid = valid & ParameterUtils::ReadUInt32(&parentID, buffer, length, &modePos);
                        ALOGE("LiuBo, id: %d, mode: %d, subMode: %d, group: %d, parentID: %d", id, mode, subMode, group, parentID);

                        if (id >= modCount)
                        {
                            SetError("Module ID exceeds count");
                            continue;
                        }

                        m_modes[id].Initialize(id, mode, subMode);

                        if (m_rootMode == NULL)
                        {
                            m_rootMode = &m_modes[id];
                        }
                        else
                        {
                            ModeEntry* parent = &m_modes[parentID];
                            if (parent != NULL)
                            {
                                parent->AddMode(&m_modes[id]);
                            }
                            else
                            {
                                valid = FALSE;
                            }
                        }
                    }
                }
            }
            else
            {
                // Create default Mode Table
                m_modes[0].Initialize(0, 0, 0);
                m_rootMode = &m_modes[0];
            }

            if (symbolOffset > 0 && symbolSize > 0 && dataOffset > 0 && dataSize > 0)
            {
                ParameterFileSymbolTable symbolTable;
                *pos   = symbolOffset;
                result = result && symbolTable.Load(buffer, length, symbolSize, pos, dataOffset);
                ALOGE("LiuBo symbolTable.ModuleCount: %d", symbolTable.ModuleCount);

                if (result && symbolTable.ModuleCount() > 0)
                {
                    UINT32 modules = symbolTable.ModuleCount();

                    for (UINT32 i = 0; i < modules; i++)
                    {
                        if (!AddModule(CreateModule(symbolTable.GetModule(i))))
                        {
                            Valid  = FALSE;
                            result = FALSE;
                        }
                    }
                }
            }
        }

        return result;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// SetError
    ///
    /// @brief Reports an error
    ///
    /// @param message  Binary parameter buffer
    /// @param entry    Symbol table entry containing the error
    /// @param module   Parameter module containing the error
    ///
    /// @return None
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    void SetError(
        const CHAR* message,
        const ParameterFileSymbolTableEntry* entry = NULL,
        const ParameterModule* module = NULL)
    {
        PARAMETER_STRCPY(Error, ERROR_LEN, message);

        if (module != NULL)
        {
            PARAMETER_SPRINTF(Error, ERROR_LEN, "%s Module: %s, Major: %u, Minor: %u, Patch: %u, Mode: %u",
                message, module->Name, module->Major, module->Minor, module->Patch, module->Mode);
        }
        else if (entry != NULL)
        {
            PARAMETER_SPRINTF(Error, ERROR_LEN, "%s Type: %s, Major: %u, Minor: %u, Patch: %u, Mode: %u",
                message, entry->Type, entry->Major, entry->Minor, entry->Patch, entry->Mode);
        }
        else
        {
            PARAMETER_STRCPY(Error, ERROR_LEN, message);
        }
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// CreateModule
    ///
    /// @brief Creates a new parameter module from a symbol table entry
    ///
    /// @param entry    Symbol table entry
    ///
    /// @return Parameter module
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ParameterModule* CreateModule(
        ParameterFileSymbolTableEntry* entry)
    {
        ParameterModule*       result = NULL;
        const ParameterModule* module = GetDefaultModule(entry->Type);

        if (module != NULL)
        {
            result = module->Parse(this, entry);
            if (result == NULL)
            {
                SetError("Failed to parser binary header data for: ", entry, module);
            }
        }
        else
        {
            SetError("Failed to find header module: ", entry, module);
        }
        return result;
    }

    virtual const ParameterModule* GetDefaultModule(char* type) = 0;

    ModeEntry*    m_rootMode;
    ModeEntry*    m_modes;
};

#endif
